/****************************************************************
 ** Product  :   HP Subscriber Network Application Policy
 ** Module   :   gr-manager
 ** Date: Apr 10, 2015               
 ** Author: Hua Zhen
 ** (C) Copyright 2015, Hewlett-Packard Company, All Rights Reserved.
 ** This software is the proprietary information of HP, Inc.  
 ** Use is subject to license terms.
 *****************************************************************
 ** Description:   
 **   Start the DR agent process for specific DB instance;
 **    
 ****************************************************************/
package com.hp.snap.gr.action.impl;

import com.google.gson.Gson;
import com.hp.snap.gr.db.voltdb.ControlDRAgent;
import com.hp.snap.gr.db.voltdb.VoltDBClientConfig;
import com.hp.snap.gr.exception.ActionException;
import com.hp.snap.gr.model.Database;
import com.hp.snap.gr.model.Server;
import com.hp.snap.gr.model.jaxb.ActionType;
import com.hp.snap.gr.model.jaxb.GeneralActionType;
import com.hp.snap.gr.model.jaxb.ParameterType;
import com.hp.snap.gr.utils.Constants;
import com.hp.snap.gr.utils.GRMConfigManager;
import com.hp.snap.gr.utils.RESTfulUtils;
import com.hp.snap.gr.utils.Utils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Map;

/**
 * 
 * Start the DR agent process for specific DB instance.
 * 
 * There is a parameter can be configured in the action: DB_INSTANCE_NAME.
 * DB_INSTANCE_NAME: The VoltDB DB instance name, such as: spr_db, session_db, lb_db;.
 * 
 * <P>
 * Version: 3.3 <br>
 * Author: Hua Zhen
 * Date: Apr 10, 2015
 * 
 * </P>
 * 
 **/
public class StartDRAgentActionHandler extends AbstractActionHandler {
    private static Logger logger = LoggerFactory.getLogger(StartDRAgentActionHandler.class);

    @Override
    public void execute(ActionType action,
                        Map<String, Object> actionPlanInputs,
                        Map<String, Object> actionPlanOutputs) throws ActionException {
        GeneralActionType actionType = (GeneralActionType) action;
        List<ParameterType> parameterTypes = actionType.getParameter();
        Map<String, String> parameterMap = Utils.contructParamMap(parameterTypes);

        /**
         * get the db instance name, which should be configured in: /SNAP/GR/<DEPLOY_NAME>/VOLTDB/<DB_INSTANCE_NAME>, e.g.
         * /SNAP/GR/Site_A1/VOLTDB/session_db]
         */
        String dbInstName = parameterMap.get("DB_INSTANCE_NAME");
        if (StringUtils.isEmpty(dbInstName)) {
            throw new ActionException("The parameter [DB_INSTANCE_NAME] should be configured!");
        }

        // find out the target database profile
        Database currentDatabase = RESTfulUtils.getVoltDBDatabase(dbInstName);

        if (logger.isTraceEnabled()) {
            logger.trace("database = {" + new Gson().toJson(currentDatabase, Database.class) + "}");
        }
        if (currentDatabase == null) {
            throw new ActionException("The database instance is not exist: " + dbInstName);
        }

        // get one available server in the hosts list
        String localServerHost = null;
        int localServerPort = 0;
        if (currentDatabase.getServers() == null || currentDatabase.getServers().size() < 1) {
            throw new ActionException("There is not any server logged in the hosts list of database ");
        }

        for (Server server : currentDatabase.getServers()) {
            if (server.getStatus().equals("ONLINE")) {
                localServerHost = server.getHost();
                localServerPort = currentDatabase.getPort();
                break;
            }
        }

        String status = currentDatabase.getStatus();
        boolean replica = currentDatabase.isReplica();

        if (logger.isTraceEnabled()) {
            logger.trace("db status=" + status + " replica=" + replica);
        }
        // get remote site name
        String remoteSiteName = Utils.getCorrespondSiteName(GRMConfigManager.getSiteName());

        if (logger.isDebugEnabled()) {
            logger.debug("[StartDRAgentActionHandler] localSiteName = {}, remoteSiteName = {}", GRMConfigManager.getSiteName(), remoteSiteName);
        }

        // get remote site database profiles
        Map<String, Database> databases = RESTfulUtils.getVoltDBDatabases(remoteSiteName);
        if (databases.isEmpty()) {
            throw new ActionException("Cannot get remote site DB profiles!");
        } else {
            if (logger.isDebugEnabled()) {
                logger.debug("[StartDRAgentActionHandler] remote databases={}", new Gson().toJson(databases, Map.class));
            }
        }

        // get remote database
        Database remoteDatabase = databases.get(dbInstName);
        if (remoteDatabase == null) {
            throw new ActionException("DB instance [" + dbInstName + "] cannot be found in remote site[" + remoteSiteName + "]!");
        } else {
            if (logger.isDebugEnabled()) {
                logger.debug("[StartDRAgentActionHandler] remote site[{}] database={}", dbInstName, new Gson().toJson(remoteDatabase, Database.class));
            }
        }

        if (logger.isDebugEnabled()) {
            logger.debug("remoteDatabase info=[" + new Gson().toJson(remoteDatabase, Database.class) + "]");
        }
        // get remote db configuration
        List<VoltDBClientConfig> dbClientConfig = ControlDRAgent.getVoltDBClientConfig(remoteDatabase, remoteSiteName);

        if (dbClientConfig == null || dbClientConfig.size() < 1) {
            throw new ActionException("Can not get configurations of [" + remoteDatabase + "] in remote site[" + remoteSiteName + "]!");
        }
        if (Constants.VDB_STATUS_ONLINE.equals(status)) {
            if (!replica) {
                throw new ActionException("The DB Instance [" + dbInstName + "] is not running in replica mode!");
            } else {
                // check DR running status
                boolean dragentStatus = ControlDRAgent.isDRAgentEnabled(remoteDatabase.getReplicationport(), false);
                if (logger.isTraceEnabled()) {
                    logger.trace("dragentStatus=" + dragentStatus);
                }
                if (!dragentStatus) {
                    ControlDRAgent.stopDRAgent(remoteDatabase.getReplicationport());
                    boolean isDRStarted = ControlDRAgent.startDRAgent(dbInstName, dbClientConfig.get(0).getUrl().substring(7, dbClientConfig.get(0).getUrl().indexOf(":", 6)),
                            String.valueOf(remoteDatabase.getReplicationport()),
                            localServerHost, String.valueOf(localServerPort), dbClientConfig.get(0).getUser(), dbClientConfig.get(0).getPassword());
                    if (!isDRStarted) {
                        throw new ActionException("DR canot be started");
                    }
                    logger.info("The DR agent over DB instance={} started", dbInstName);
                } else {
                    logger.info("The DR agent has already been running");
                }
            }
        } else {
            logger.warn("The database is in status [{}], and do not process!", status);
        }

    }
}
